//
//  XLGDBUpdateOperation.m
//  etionCBLogic
//
//  Created by wjxfhxy on 15/10/30.
//  Copyright (c) 2015年 GuangZhouXuanWu. All rights reserved.
//

#import "XLGDBUpdateOperation.h"

#define XLGDBUpdateOperationDBUpdateFile          @"dbupdate.plist"

#define XLGDBUpdateOperationDBName          @"DBName"

#define XLGDBUpdateOperationDBPath          @"DBPath"

#define XLGDBUpdateOperationTable           @"Table"

#define XLGDBUpdateOperationTableStatus     @"TableStatus"

#define XLGDBUpdateOperationTableName       @"TableName"

#define XLGDBUpdateOperationCols            @"Cols"

#define XLGDBUpdateOperationAddCols            @"Add"

#define XLGDBUpdateOperationModifyCols            @"Modify"

#define XLGDBUpdateOperationDefaultValue           @"DefaultValue"

#define XLGDBUpdateOperationColType            @"ColType"

#define XLGDBUpdateOperationPrimaryKey           @"PrimaryKey"

#define XLGDBUpdateOperationColName           @"ColName"

@implementation XLGDBUpdateOperation

-(NSArray*)readPlist
{
    NSString *path = [[NSBundle mainBundle] pathForResource:@"dbupdate" ofType:@"plist"];
    NSArray *array = [NSArray arrayWithContentsOfFile:path];
    return array;
}

-(void)operateDBUpdata:(NSDictionary *)updateInfo
{
    if(updateInfo.count <=0 )
        return;
    
    NSArray *eDirs = nil;
    [XMSFileHelper isDirEmpty:[XMSFileHelper documentPath] containFiles:&eDirs];
    
    NSMutableArray *tmp = [eDirs mutableCopy];
    for(NSString *eDir in eDirs)
    {
        if([NSString isNumberString:eDir] == NO)
            [tmp removeObject:eDir];
    }
    
    eDirs = tmp;
    
    NSDictionary *dic = updateInfo;
    
    for(NSString *eDir in eDirs)
    {
        @autoreleasepool
        {
            NSString *dbName = dic[XLGDBUpdateOperationDBName];
            
            if(dbName.length <=0 )
                continue;
            
            NSString *dbPath = [NSString pathWithComponents:@[[XMSFileHelper documentPath],eDir,dbName]];
            
            NSArray *tableArray = dic[XLGDBUpdateOperationTable];
           
            if(tableArray.count <= 0)
                continue;
            
            if(tableArray.count > 0)
            {
                [self updateDB:dbPath operateCols:^(NSString **tableName,NSDictionary **cols,NSUInteger *index)
                {
                    if(*index >= tableArray.count)
                    {
                        *index = NSNotFound;
                    }
                    else
                    {
                        NSDictionary *tableDic = tableArray[*index];
                        
                        *tableName = tableDic[XLGDBUpdateOperationTableName];
                        *cols = tableDic[XLGDBUpdateOperationCols];
                    }
                }];
            }
        }
    }
}

-(void)updateDB:(NSString*)dbPath operateCols:(void(^)(NSString **tableName,NSDictionary **cols,NSUInteger *index))operateNewCols
{
    XMSSQLiteHelper *sqlDis = [XMSSQLiteHelper new];
    [sqlDis openTable:dbPath flags:SQLITE_OPEN_READWRITE| SQLITE_OPEN_FULLMUTEX];
    
    XMSSQLiteHelper *sqlOri = [XMSSQLiteHelper new];
    [sqlOri openTable:[[NSBundle mainBundle] pathForResource:[[dbPath lastPathComponent] stringByDeletingPathExtension] ofType:@"db"] flags:SQLITE_OPEN_READWRITE| SQLITE_OPEN_FULLMUTEX];
    
    for(NSUInteger i = 0; i < NSNotFound;i++)
    {
        NSString *tableName = @"";
        NSDictionary *cols = @{};
        
        operateNewCols(&tableName,&cols,&i);
        if(i >= NSNotFound)
            break;
        
        BOOL result = NO;
        
        __block NSString *disSQLstr = @"";
        [sqlDis selectTable:[[NSString stringWithFormat:@"select sql from sqlite_master where type = 'table' and name = '%@'", tableName] UTF8String] gettingTable:^(BOOL *bStop)
        {
            disSQLstr = [sqlDis getFieldNSString:0];
        }];
        
        NSMutableDictionary *modifyCols = [cols[XLGDBUpdateOperationModifyCols] mutableCopy];
        
        if(disSQLstr.length <= 0 && modifyCols.count <=0)
        {
            continue;
        }
        else
        {
            NSArray *oriCols = [modifyCols allKeys];
            if([oriCols containsObject:@"ALLCOLS"])
            {
                BOOL exist = [sqlDis table:@"sqlite_master" containDataWithCondition:[NSString stringWithFormat:@"type = 'table' and name = '%@'",tableName]];
                if(exist == NO)
                {
                    __block NSString *oriSQLstr = @"";
                    [sqlOri selectTable:[[NSString stringWithFormat:@"select sql from sqlite_master where type = 'table' and name = '%@'", tableName] UTF8String] gettingTable:^(BOOL *bStop)
                    {
                        oriSQLstr = [sqlOri getFieldNSString:0];
                    }];
                    
                    [sqlDis immediateRunSQL:[oriSQLstr UTF8String]];
                    
                    continue;
                }
            }
            
            [modifyCols removeObjectForKey:@"ALLCOLS"];
            oriCols = [modifyCols allKeys];
            if(oriCols.count > 0)
            {
                NSArray *oriTableCols = [sqlOri SQLiteTableHeads:tableName];
                NSArray *disTableCols = [sqlDis SQLiteTableHeads:tableName];
                
                NSMutableArray *news = [NSMutableArray new];
                NSMutableDictionary *modifys = [NSMutableDictionary new];
                
                for(NSString *oriCol in oriCols)
                {
                    NSArray *disCols = [modifyCols valueForKey:oriCol];
                    
                    //如果目标字段在当前实际目标表中不存在，则认为是全新的字段
                    BOOL find = NO;
                    for(XMSTableCol *col in disTableCols)
                    {
                        if([disCols containsObject:col.colName] == YES)
                        {
                            find = YES;
                            
                            //如果目标字段在当前实际目标表中存在，且两字段相同，比较是否是主键
                            if([oriCol isEqualToString:col.colName])
                            {
                                for(XMSTableCol *c in oriTableCols)
                                {
                                    if([c.colName isEqualToString:col.colName])
                                    {
                                        if(c.primaryKey != col.primaryKey)
                                            modifys[oriCol] = col.colName;
                                        break;
                                    }
                                }
                            }
                            //如果目标字段在当前实际目标表中存在，则认为是需要修改的字段
                            else
                            {
                                modifys[oriCol] = col.colName;
                            }
                            
                            break;
                        }
                    }
                    if(find == NO)
                    {
                        [news addObject:oriCol];
                    }
                }
                
                [modifyCols removeObjectsForKeys:news];
            
                if(modifys.count > 0)
                {
                    NSString *tmpTableName = [NSString stringWithFormat:@"tmp_%@",tableName];
                    
                    __block NSString *oriSQLstr = @"";
                    [sqlOri selectTable:[[NSString stringWithFormat:@"select sql from sqlite_master where type = 'table' and name = '%@'", tableName] UTF8String] gettingTable:^(BOOL *bStop)
                    {
                        oriSQLstr = [sqlOri getFieldNSString:0];
                    }];
                    
                    oriSQLstr = [NSString replaceString:oriSQLstr replacekv:@{tableName:tmpTableName}];
                    
                    result = [sqlDis immediateRunSQL:[oriSQLstr UTF8String]];
                    
                    if(result == YES)
                    {
                        NSMutableArray *disCols = [[disTableCols fetchArrayObjectPorpertyValueWithPorperty:@"colName"] mutableCopy];
                        NSMutableArray *oriCols = [[oriTableCols fetchArrayObjectPorpertyValueWithPorperty:@"colName"] mutableCopy];
                        
                        //去掉旧表中不存在于新表的字段
                        for(NSString *disCol in [disCols copy])
                        {
                            if([oriCols containsObject:disCol] != YES)
                                [disCols removeObject:disCol];
                        }
                        
                        oriCols = [disCols mutableCopy];
                        for(NSString *key in [modifys allKeys])
                        {
                            if([disCols containsObject:key] == YES)
                            {
                                [oriCols replaceObjectAtIndex:[oriCols indexOfObject:key] withObject:modifys[key]];
                            }
                            else
                            {
                                [disCols addObject:modifys[key]];
                                [oriCols addObject:key];
                            }
                        }
                        
                        
                        NSString *insertStr = [NSString stringWithFormat:@"insert into %@(%@) select %@ from %@",tmpTableName,[NSString megerStringWithTag:oriCols tag:@","],[NSString megerStringWithTag:disCols tag:@","],tableName];
                        
                        result = [sqlDis transExce:^NSInteger
                        {
                            BOOL result = NO;
                            
                            result = [sqlDis immediateExecSQL:[insertStr UTF8String]];
                            
                            if(result == YES)
                                result = [sqlDis immediateExecSQL:[[NSString stringWithFormat:@"drop table %@",tableName] UTF8String]];
                            
                            if(result == YES)
                                result = [sqlDis immediateExecSQL:[[NSString stringWithFormat:@"alter table %@ rename to %@",tmpTableName,tableName] UTF8String]];
                            
                            return result;
                        }];
                    }
                }
                else if(news.count > 0)
                {
                    NSMutableArray *ar = [NSMutableArray new];
                    for(NSString *new in news)
                    {
                        for(XMSTableCol *tableCol in oriTableCols)
                        {
                            if([tableCol.colName isEqualToString:new])
                            {
                                NSString *sqlString = [NSString stringWithFormat:@"alter table %@ add column %@ %@ %@",tableName,tableCol.colName,tableCol.colTypeText,tableCol.primaryKey ? @"primary key" : @""];
                                
                                [ar addObject:sqlString];
                            }
                        }
                    }
                    
                    result = [sqlDis transExce:^NSInteger
                    {
                        BOOL result = NO;
                        for(NSString *sqlString in ar)
                        {
                            result = [sqlDis immediateRunSQL:[sqlString UTF8String]];
                            if(result != YES)
                                break;
                        }
                        return result;
                    }];
                }
            }
        }
    }
    
    [sqlDis closeTable];
    [sqlOri closeTable];
}

+(void)updateDBs:(NSArray*)dbFileNames operationCompletion:(void(^)())completion
{
    DispatchThread(^
    {
        XLGDBUpdateOperation *update = [XLGDBUpdateOperation new];
        NSArray *plist = [update readPlist];
        for(NSString *dbFileName in dbFileNames)
        {
            for(NSDictionary *dic in plist)
            {
                if([dic[XLGDBUpdateOperationDBName] isEqualToString:dbFileName])
                    [update operateDBUpdata:dic];
            }
        }
        DispatchMainThread(NO,completion);
    });
}

@end
